package com.bestcem.xm.member.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.common.core.uitls.EnumUtil;
import com.bestcem.xm.componet.redis.service.RedisService;
import com.bestcem.xm.member.constant.ColumnConstant;
import com.bestcem.xm.member.dao.SyncRuleDao;
import com.bestcem.xm.member.entity.mongo.SyncRule;
import com.bestcem.xm.member.entity.pojo.SyncRuleDO;
import com.bestcem.xm.member.enums.MemberTypeEnum;
import com.bestcem.xm.member.enums.SyncTypeEnum;
import com.bestcem.xm.member.grpc.service.param.syncrule.BatchUpdateSyncRuleParam;
import com.bestcem.xm.member.grpc.service.param.syncrule.CreateSyncRuleParam;
import com.bestcem.xm.member.grpc.service.param.syncrule.GetSyncRuleParam;
import com.bestcem.xm.member.grpc.service.param.syncrule.UpdateSyncRuleParam;
import com.bestcem.xm.member.service.SyncRuleService;
import com.bestcem.xm.member.service.WechatService;
import com.bestcem.xm.member.service.dto.SyncRuleDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.bestcem.xm.common.core.constant.Constants.SYNC_RULE_CACHE_KEY;

/**
 * @author ming.xue <ming.xue@idiaoyan.com>
 * @desc 同步规则服务
 * @date 2021-08-02
 */

@Slf4j
@Service
public class SyncRuleServiceImpl implements SyncRuleService {

    /**
     * 同步规则数据访问
     */
    @Autowired
    private SyncRuleDao syncRuleDao;

    @Resource
    private RedisService redisService;

    @Resource
    private WechatService wechatService;

    /**
     * 获取同步规则
     *
     * @param param
     * @return
     */
    @Override
    public ServiceResult<SyncRuleDTO> getSyncRule(GetSyncRuleParam param) {
        // 校验联系人类型
        List<Integer> supportedMemberTypes = Arrays.asList(MemberTypeEnum.INDIVIDUAL.getIndex(),
                MemberTypeEnum.INSTITUTION.getIndex(),
                MemberTypeEnum.INTERIOR.getIndex());
        Integer memberType = param.getMemberType();
        if (!supportedMemberTypes.contains(memberType)) {
            return ServiceResult.fail("联系人类型错误");
        }

        // 根据条件获取同步规则
        String orgId = param.getOrgId();
        SyncRuleDO syncRuleEntity = syncRuleDao.getByOrderIdAndMemberType(orgId, memberType);
        if (null != syncRuleEntity) {
            return ServiceResult.success(this.toSyncRuleDTO(syncRuleEntity));
        }

        SyncRuleDTO syncRuleDTO = new SyncRuleDTO();
        syncRuleDTO.setMemberType(memberType);
        syncRuleDTO.setType(null);
        syncRuleDTO.setQueryFields(Arrays.asList(ColumnConstant.Member.MOBILE, ColumnConstant.Member.EMAIL));
        // 默认是 不同步
        syncRuleDTO.setWechatSyncType(SyncTypeEnum.NEVER.getIndex());
        return ServiceResult.success(syncRuleDTO);
    }

    /**
     * 创建同步规则
     *
     * @param param
     * @return
     */
    @Override
    public ServiceResult<String> createSyncRule(CreateSyncRuleParam param) {
        // 校验联系人类型
        Integer memberType = param.getMemberType();
        MemberTypeEnum memberTypeEnum = EnumUtil.getEnumByIndex(MemberTypeEnum.class, memberType);
        if (memberTypeEnum == null) {
            return ServiceResult.fail("联系人类型无效");
        }

        // 校验同步规则是否已经存在
        String orgId = param.getOrgId();
        SyncRuleDO syncRuleDo = syncRuleDao.getByOrderIdAndMemberType(orgId, memberType);
        if (syncRuleDo != null) {
            return ServiceResult.fail("同步规则已存在, 请前往编辑");
        }

        // 校验联系人同步类型
        Integer syncType = param.getSyncType();
        SyncTypeEnum syncTypeEnum = EnumUtil.getEnumByIndex(SyncTypeEnum.class, syncType);
        if (syncTypeEnum == null) {
            return ServiceResult.fail("答卷同步类型无效");
        }

        // 校验联系人同步类型
        Integer wechatSyncType = param.getWechatSyncType();
        SyncTypeEnum wechatSyncTypeEnum = EnumUtil.getEnumByIndex(SyncTypeEnum.class, wechatSyncType);
        if (wechatSyncTypeEnum == null) {
            return ServiceResult.fail("微信同步类型无效");
        }

        // 处理匹配字段
        List<String> queryFields = param.getQueryFields();
        if (CollectionUtils.isEmpty(queryFields)) {
            queryFields = Arrays.asList("mobile");
        }
        List<String> updateFields = param.getUpdateFields();

        // 保存同步规则
        syncRuleDo = fillDefaultValue(new SyncRuleDO());
        syncRuleDo.setOrgId(orgId);
        syncRuleDo.setMemberType(memberType);
        syncRuleDo.setType(syncType);
        syncRuleDo.setWechatSyncEnabled(wechatSyncType);
        if (!CollectionUtils.isEmpty(queryFields)) {
            syncRuleDo.setQueryFields(JSONUtil.toJsonStr(queryFields));
        } else {
            syncRuleDo.setQueryFields(JSONUtil.toJsonStr(new ArrayList<>()));
        }
        if (!CollectionUtils.isEmpty(updateFields)) {
            syncRuleDo.setUpdateFields(JSONUtil.toJsonStr(updateFields));
        } else {
            syncRuleDo.setUpdateFields(JSONUtil.toJsonStr(new ArrayList<>()));
        }
        syncRuleDao.create(syncRuleDo);
        // 开始微信同步
        sendWxSyncMessage(param.getWechatSyncType(), syncRuleDo.getOrgId(), syncRuleDo.getMemberType());
        return ServiceResult.success(syncRuleDo.getSyncruleId());
    }

    /**
     * 修改同步规则
     *
     * @param param
     * @return
     */
    @Override
    public ServiceResult<SyncRuleDTO> updateSyncRule(UpdateSyncRuleParam param) {
        String id = param.getId();
        SyncRuleDO syncRuleEntity = syncRuleDao.findBySyncRuleId(id);
        if (null == syncRuleEntity) {
            return ServiceResult.fail("同步规则标识无效");
        }

        // 校验联系人同步类型
        Integer syncType = param.getSyncType();
        SyncTypeEnum syncTypeEnum = EnumUtil.getEnumByIndex(SyncTypeEnum.class, syncType);
        if (syncTypeEnum == null) {
            return ServiceResult.fail("答卷同步类型无效");
        }

        Integer wechatSyncType = param.getWechatSyncType();
        SyncTypeEnum wechatSyncTypeEnum = EnumUtil.getEnumByIndex(SyncTypeEnum.class, wechatSyncType);
        if (wechatSyncTypeEnum == null) {
            return ServiceResult.fail("微信同步类型无效");
        }

        // 处理匹配字段
        List<String> queryFields = param.getQueryFields();
        if (CollectionUtils.isEmpty(queryFields)) {
            queryFields = Arrays.asList("mobile");
        }
        List<String> updateFields = param.getUpdateFields();

        // 保存同步规则
        SyncRuleDO update = new SyncRuleDO();
        update.setType(syncType);
        update.setWechatSyncEnabled(wechatSyncType);
        if (!CollectionUtils.isEmpty(queryFields)) {
            update.setQueryFields(JSONUtil.toJsonStr(queryFields));
        }
        if (!CollectionUtils.isEmpty(updateFields)) {
            update.setUpdateFields(JSONUtil.toJsonStr(updateFields));
        }
        update.setSyncruleId(id);
        syncRuleDao.update(update);
        // 发布消息 本地缓存更新
        this.flushRedisCache(syncRuleEntity.getOrgId(), syncRuleEntity.getMemberType(), syncType);
        // 开始微信同步
        sendWxSyncMessage(param.getWechatSyncType(), syncRuleEntity.getOrgId(), syncRuleEntity.getMemberType());
        return ServiceResult.success(this.toSyncRuleDTO(syncRuleEntity));
    }

    /**
     * 获取同步规则默认字段列表
     *
     * @param memberType
     * @return
     */
    @Override
    public ServiceResult<Map<String, List<String[]>>> listDefaultSyncRuleFields(Integer memberType) {
        List<Integer> supportedMemberTypes = Arrays.asList(MemberTypeEnum.INDIVIDUAL.getIndex(),
                MemberTypeEnum.INSTITUTION.getIndex(),
                MemberTypeEnum.INTERIOR.getIndex());
        if (!supportedMemberTypes.contains(memberType)) {
            return ServiceResult.fail("联系人类型无效");
        }

        // 匹配字段列表
        List<String[]> queryFields = new ArrayList<>(4);
        queryFields.add(new String[]{"邮箱", "email"});
        queryFields.add(new String[]{"手机号", "mobile"});
        queryFields.add(new String[]{"微信", "openid"});
        queryFields.add(new String[]{"支付宝", "alipay_user_id"});

        // 更新字段列表
        List<String[]> updateFields = new ArrayList<>(16);
        updateFields.add(new String[]{"用户名", "name"});
        updateFields.add(new String[]{"邮箱", "email"});
        updateFields.add(new String[]{"手机号", "mobile"});
        updateFields.add(new String[]{"性别", "gender"});
        updateFields.add(new String[]{"昵称", "nickname"});
        updateFields.add(new String[]{"地区", "region"});
        updateFields.add(new String[]{"门店", "store"});
        updateFields.add(new String[]{"备注", "content"});
        updateFields.add(new String[]{"微信", "openid"});
        updateFields.add(new String[]{"支付宝", "alipay_user_id"});
        updateFields.add(new String[]{"联系人编号", "memberNo"});
        updateFields.add(new String[]{"地址信息", "address"});
        if (Objects.equals(memberType, MemberTypeEnum.INDIVIDUAL.getIndex())) {
            updateFields.add(new String[]{"生日", "birthday"});
        } else {
            updateFields.add(new String[]{"员工编号", "employeeID"});
            updateFields.add(new String[]{"生日", "birthday"});
            updateFields.add(new String[]{"所属部门", "department"});
            updateFields.add(new String[]{"职位", "position"});
            updateFields.add(new String[]{"机构名称", "institut_name"});
            updateFields.add(new String[]{"机构id", "institutID"});
        }

        // 处理返回结果
        Map<String, List<String[]>> defaultSyncRuleFields = new HashMap<>(4);
        defaultSyncRuleFields.put("q_field", queryFields);
        defaultSyncRuleFields.put("u_field", updateFields);
        return ServiceResult.success(defaultSyncRuleFields);
    }

    /**
     * 重置同步规则
     *
     * @param id
     * @return
     */
    @Override
    public ServiceResult<SyncRuleDTO> restoreSyncRule(String id) {
        SyncRuleDO syncRuleEntity = syncRuleDao.findBySyncRuleId(id);
        if (null == syncRuleEntity) {
            return ServiceResult.fail("同步规则标识无效");
        }
        // 重置同步规则
        syncRuleEntity.setQueryFields(JSONUtil.toJsonStr(Arrays.asList(ColumnConstant.Member.MOBILE, ColumnConstant.Member.EMAIL)));
        syncRuleEntity.setUpdateFields("");
        syncRuleEntity.setType(SyncTypeEnum.NEVER.getIndex());
        syncRuleEntity.setWechatSyncEnabled(SyncTypeEnum.NEVER.getIndex());
        syncRuleEntity.setUpdateTime(DateUtil.getCommonDate());
        syncRuleDao.update(syncRuleEntity);

        // 发布更新本地缓存
        this.flushRedisCache(syncRuleEntity.getOrgId(), syncRuleEntity.getMemberType(), syncRuleEntity.getType());
        SyncRuleDTO syncRuleDTO = this.toSyncRuleDTO(syncRuleEntity);
        return ServiceResult.success(syncRuleDTO);
    }

    @Override
    public ServiceResult<Void> batchUpdateSyncRule(BatchUpdateSyncRuleParam param) {
        if (CollUtil.isNotEmpty(param.getRules())) {
            List<SyncRuleDO> syncRuleDOS = param.getRules().stream().map(rule -> {
                SyncRuleDO syncRuleDo = new SyncRuleDO();
                syncRuleDo.setMemberType(rule.getMemberType());
                syncRuleDo.setType(rule.getSyncType());
                syncRuleDo.setOrgId(rule.getOrgId());
                syncRuleDo.setWechatSyncEnabled(rule.getWechatSyncType());
                return syncRuleDo;
            }).collect(Collectors.toList());
            syncRuleDao.batchUpdateSyncRule(syncRuleDOS);
            param.getRules().forEach(rule -> {
                // 发布更新本地缓存
                this.flushRedisCache(rule.getOrgId(), rule.getMemberType(), rule.getSyncType());
                // 开始微信同步
                this.sendWxSyncMessage(rule.getWechatSyncType(), rule.getOrgId(), rule.getMemberType());
            });
        }
        return ServiceResult.success();
    }

    /**
     * 类型转换
     *
     * @param syncRule
     * @return
     */
    private SyncRuleDTO toSyncRuleDTO(SyncRule syncRule) {
        SyncRuleDTO syncRuleDTO = new SyncRuleDTO();
        if (syncRule == null) {
            return syncRuleDTO;
        }

        syncRuleDTO.setId(syncRule.getId());
        syncRuleDTO.setMemberType(syncRule.getMemberType());
        syncRuleDTO.setType(syncRule.getType());
        syncRuleDTO.setQueryFields(syncRule.getQueryFields());
        syncRuleDTO.setUpdateFields(syncRule.getUpdateFields());
        syncRuleDTO.setCreateTime(syncRule.getCreateTime());
        syncRuleDTO.setUpdateTime(syncRule.getUpdateTime());
        return syncRuleDTO;
    }

    /**
     * 类型装换
     *
     * @param syncRuleEntity do
     * @return dto
     */
    private SyncRuleDTO toSyncRuleDTO(SyncRuleDO syncRuleEntity) {
        SyncRuleDTO syncRuleDTO = new SyncRuleDTO();
        if (null == syncRuleEntity) {
            return syncRuleDTO;
        }

        syncRuleDTO.setId(syncRuleEntity.getSyncruleId());
        syncRuleDTO.setMemberType(syncRuleEntity.getMemberType());
        syncRuleDTO.setType(syncRuleEntity.getType());
        syncRuleDTO.setWechatSyncType(syncRuleEntity.getWechatSyncEnabled());
        if (StringUtils.hasText(syncRuleEntity.getQueryFields())) {
            syncRuleDTO.setQueryFields(parseListT(syncRuleEntity.getQueryFields(), String.class));
        }
        if (StringUtils.hasText(syncRuleEntity.getUpdateFields())) {
            syncRuleDTO.setUpdateFields(parseListT(syncRuleEntity.getUpdateFields(), String.class));
        }
        syncRuleDTO.setCreateTime(syncRuleEntity.getCreateTime());
        syncRuleDTO.setUpdateTime(syncRuleEntity.getUpdateTime());
        return syncRuleDTO;
    }

    /**
     * 填充默认值
     *
     * @param syncRuleEntity 同步规则信息
     * @return 同步规则信息
     */
    private SyncRuleDO fillDefaultValue(SyncRuleDO syncRuleEntity) {
        if (null != syncRuleEntity) {
            syncRuleEntity.setCreateTime(DateUtil.getCommonDate());
            syncRuleEntity.setUpdateTime(syncRuleEntity.getCreateTime());
            syncRuleEntity.setMemberType(MemberTypeEnum.INDIVIDUAL.getIndex());
        }
        return syncRuleEntity;
    }

    /**
     * json解析成指定类型的List对象
     *
     * @param json
     * @param tClass
     * @param <T>
     * @return
     */
    private static <T> List<T> parseListT(String json, Class<T> tClass) {
        if (!StringUtils.hasText(json)) {
            return new ArrayList<>();
        }
        if (!JSONUtil.isJsonArray(json)) {
            json = new StringBuilder("[").append(json).append("]").toString();
        }
        return JSONUtil.toList(JSONUtil.parseArray(json), tClass);
    }

    @Override
    public void flushRedisCache(String orgId, Integer memberType, Integer type) {
        String key = StrUtil.format(SYNC_RULE_CACHE_KEY, orgId, memberType);
        redisService.del(key);
    }

    @Override
    public Integer get(String orgId, int memberType) {
        String key = StrUtil.format(SYNC_RULE_CACHE_KEY, orgId, memberType);
        Object value = redisService.get(key);
        if (ObjectUtil.isEmpty(value)) {
            SyncRuleDO syncRule = syncRuleDao.getByOrderIdAndMemberType(orgId, memberType);
            Integer syncRuleValue = syncRule != null ? syncRule.getType() : 0;
            redisService.set(key, syncRuleValue);
            return syncRuleValue;
        }
        return Integer.parseInt(String.valueOf(value));
    }

    /**
     * 发送消息
     * 考虑到 每天一个租户只能一次 且就调一个接口 所以 用同步
     *
     * @param wechatSyncEnabled
     * @param orgId
     * @param memberType
     */
    private void sendWxSyncMessage(Integer wechatSyncEnabled, String orgId, Integer memberType) {
        if (wechatSyncEnabled == 0) {
            // 微信同步开启
            // 从redis中获取是否当天开启过
            String key = StrUtil.format("member:wechat:sync:{}", orgId);
            Boolean hasKey = redisService.hasKey(key);
            if (!hasKey) {
                // 没有开启过
                wechatService.sendWxSyncMessage(orgId);
                // 设置当天开启过
                // 获取当天剩余的时间 s
                Long ttl = DateUtil.getRemainingTimeToday();
                redisService.set(key, 1, ttl, TimeUnit.SECONDS);
            }
        }
    }

}
